const querystring = require('node:querystring');
const got = require('got');
const timeout = 15000;

const push_config = {
 HITOKOTO: true, // 启用一言（随机句子）

 BARK_PUSH: '', // bark IP 或设备码，例：https://api.day.app/DxHcxxxxxRxxxxxxcm/
 BARK_ARCHIVE: '', // bark 推送是否存档
 BARK_GROUP: '', // bark 推送分组
 BARK_SOUND: '', // bark 推送声音
 BARK_ICON: '', // bark 推送图标
 BARK_LEVEL: '', // bark 推送时效性
 BARK_URL: '', // bark 推送跳转URL

 DD_BOT_SECRET: '', // 钉钉机器人的 DD_BOT_SECRET
 DD_BOT_TOKEN: '', // 钉钉机器人的 DD_BOT_TOKEN

 FSKEY: '', // 飞书机器人的 FSKEY

 // 推送到个人QQ：http://127.0.0.1/send_private_msg
 // 群：http://127.0.0.1/send_group_msg
 GOBOT_URL: '', // go-cqhttp
 // 推送到个人QQ 填入 user_id=个人QQ
 // 群 填入 group_id=QQ群
 GOBOT_QQ: '', // go-cqhttp 的推送群或用户
 GOBOT_TOKEN: '', // go-cqhttp 的 access_token

 GOTIFY_URL: '', // gotify地址,如https://push.example.de:8080
 GOTIFY_TOKEN: '', // gotify的消息应用token
 GOTIFY_PRIORITY: 0, // 推送消息优先级,默认为0

 IGOT_PUSH_KEY: '', // iGot 聚合推送的 IGOT_PUSH_KEY，例如：https://push.hellyw.com/XXXXXXXX

 PUSH_KEY: '', // server 酱的 PUSH_KEY，兼容旧版与 Turbo 版

 DEER_KEY: '', // PushDeer 的 PUSHDEER_KEY
 DEER_URL: '', // PushDeer 的 PUSHDEER_URL

 CHAT_URL: '', // synology chat url
 CHAT_TOKEN: '', // synology chat token

 // 官方文档：https://www.pushplus.plus/
 PUSH_PLUS_TOKEN: '', // pushplus 推送的用户令牌
 PUSH_PLUS_USER: '', // pushplus 推送的群组编码
 PUSH_PLUS_TEMPLATE: 'html',    // pushplus 发送模板，支持html,txt,json,markdown,cloudMonitor,jenkins,route,pay
 PUSH_PLUS_CHANNEL: 'wechat',   // pushplus 发送渠道，支持wechat,webhook,cp,mail,sms
 PUSH_PLUS_WEBHOOK: '',      // pushplus webhook编码，可在pushplus公众号上扩展配置出更多渠道
 PUSH_PLUS_CALLBACKURL: '',    // pushplus 发送结果回调地址，会把推送最终结果通知到这个地址上
 PUSH_PLUS_TO: '',        // pushplus 好友令牌，微信公众号渠道填写好友令牌，企业微信渠道填写企业微信用户id


 // 微加机器人，官方网站：https://www.weplusbot.com/
 WE_PLUS_BOT_TOKEN: '', // 微加机器人的用户令牌
 WE_PLUS_BOT_RECEIVER: '', // 微加机器人的消息接收人
 WE_PLUS_BOT_VERSION: 'pro', //微加机器人调用版本，pro和personal；为空默认使用pro(专业版)，个人版填写：personal

 QMSG_KEY: '', // qmsg 酱的 QMSG_KEY
 QMSG_TYPE: '', // qmsg 酱的 QMSG_TYPE

 QYWX_ORIGIN: 'https://qyapi.weixin.qq.com', // 企业微信代理地址

 /*
  此处填你企业微信应用消息的值(详见文档 https://work.weixin.qq.com/api/doc/90000/90135/90236)
  环境变量名 QYWX_AM依次填入 corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型)
  注意用,号隔开(英文输入法的逗号)，例如：wwcff56746d9adwers,B-791548lnzXBE6_BWfxdf3kSTMJr9vFEPKAbh6WERQ,mingcheng,1000001,2COXgjH2UIfERF2zxrtUOKgQ9XklUqMdGSWLBoW_lSDAdafat
  可选推送消息类型(推荐使用图文消息（mpnews）):
  - 文本卡片消息: 0 (数字零)
  - 文本消息: 1 (数字一)
  - 图文消息（mpnews）: 素材库图片id, 可查看此教程(http://note.youdao.com/s/HMiudGkb)或者(https://note.youdao.com/ynoteshare1/index.html?id=1a0c8aff284ad28cbd011b29b3ad0191&type=note)
 */
 QYWX_AM: '', // 企业微信应用

 QYWX_KEY: '', // 企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770)，例如：693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa

 TG_BOT_TOKEN: '', // tg 机器人的 TG_BOT_TOKEN，例：1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ
 TG_USER_ID: '', // tg 机器人的 TG_USER_ID，例：1434078534
 TG_API_HOST: 'https://api.telegram.org', // tg 代理 api
 TG_PROXY_AUTH: '', // tg 代理认证参数
 TG_PROXY_HOST: '', // tg 机器人的 TG_PROXY_HOST
 TG_PROXY_PORT: '', // tg 机器人的 TG_PROXY_PORT

 AIBOTK_KEY: '', // 智能微秘书 个人中心的apikey 文档地址：http://wechat.aibotk.com/docs/about
 AIBOTK_TYPE: '', // 智能微秘书 发送目标 room 或 contact
 AIBOTK_NAME: '', // 智能微秘书 发送群名 或者好友昵称和type要对应好

 SMTP_SERVICE: '', // 邮箱服务名称，比如 126、163、Gmail、QQ 等，支持列表 https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json
 SMTP_EMAIL: '', // SMTP 收发件邮箱，通知将会由自己发给自己
 SMTP_PASSWORD: '', // SMTP 登录密码，也可能为特殊口令，视具体邮件服务商说明而定
 SMTP_NAME: '', // SMTP 收发件人姓名，可随意填写

 PUSHME_KEY: '', // 官方文档：https://push.i-i.me，PushMe 酱的 PUSHME_KEY

 // CHRONOCAT API https://chronocat.vercel.app/install/docker/official/
 CHRONOCAT_QQ: '', // 个人: user_id=个人QQ 群则填入 group_id=QQ群 多个用英文;隔开同时支持个人和群
 CHRONOCAT_TOKEN: '', // 填写在CHRONOCAT文件生成的访问密钥
 CHRONOCAT_URL: '', // Red 协议连接地址 例： http://127.0.0.1:16530

 WEBHOOK_URL: '', // 自定义通知 请求地址
 WEBHOOK_BODY: '', // 自定义通知 请求体
 WEBHOOK_HEADERS: '', // 自定义通知 请求头
 WEBHOOK_METHOD: '', // 自定义通知 请求方法
 WEBHOOK_CONTENT_TYPE: '', // 自定义通知 content-type

 NTFY_URL: '', // ntfy地址,如https://ntfy.sh,默认为https://ntfy.sh
 NTFY_TOPIC: '', // ntfy的消息应用topic
 NTFY_PRIORITY: '3', // 推送消息优先级,默认为3

 // 官方文档: https://wxpusher.zjiecode.com/docs/
 // 管理后台: https://wxpusher.zjiecode.com/admin/
 WXPUSHER_APP_TOKEN: '', // wxpusher 的 appToken
 WXPUSHER_TOPIC_IDS: '', // wxpusher 的 主题ID，多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
 WXPUSHER_UIDS: '', // wxpusher 的 用户ID，多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
};

for (const key in push_config) {
 const v = process.env[key];
 if (v) {
  push_config[key] = v;
 }
}

const $ = {
 post: (params, callback) => {
  const { url, ...others } = params;
  got.post(url, others).then(
   (res) => {
    let body = res.body;
    try {
     body = JSON.parse(body);
    } catch (error) {}
    callback(null, res, body);
   },
   (err) => {
    callback(err?.response?.body || err);
   },
  );
 },
 get: (params, callback) => {
  const { url, ...others } = params;
  got.get(url, others).then(
   (res) => {
    let body = res.body;
    try {
     body = JSON.parse(body);
    } catch (error) {}
    callback(null, res, body);
   },
   (err) => {
    callback(err?.response?.body || err);
   },
  );
 },
 logErr: console.log,
};

async function one() {
 const url = 'https://v1.hitokoto.cn/';
 const res = await got.get(url);
 const body = JSON.parse(res.body);
 return `${body.hitokoto}  ----${body.from}`;
}

function gotifyNotify(text, desp) {
 return new Promise((resolve) => {
  const { GOTIFY_URL, GOTIFY_TOKEN, GOTIFY_PRIORITY } = push_config;
  if (GOTIFY_URL && GOTIFY_TOKEN) {
   const options = {
    url: `${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}`,
    body: `title=${encodeURIComponent(text)}&message=${encodeURIComponent(
     desp,
    )}&priority=${GOTIFY_PRIORITY}`,
    headers: {
     'Content-Type': 'application/x-www-form-urlencoded',
    },
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('Gotify 发送通知调用API失败😞\n', err);
     } else {
      if (data.id) {
       console.log('Gotify 发送通知消息成功🎉\n');
      } else {
       console.log(`Gotify 发送通知调用API失败😞 ${data.message}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve();
    }
   });
  } else {
   resolve();
  }
 });
}

function gobotNotify(text, desp) {
 return new Promise((resolve) => {
  const { GOBOT_URL, GOBOT_TOKEN, GOBOT_QQ } = push_config;
  if (GOBOT_URL) {
   const options = {
    url: `${GOBOT_URL}?access_token=${GOBOT_TOKEN}&${GOBOT_QQ}`,
    json: { message: `${text}\n${desp}` },
    headers: {
     'Content-Type': 'application/json',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('Go-cqhttp 通知调用API失败😞\n', err);
     } else {
      if (data.retcode === 0) {
       console.log('Go-cqhttp 发送通知消息成功🎉\n');
      } else if (data.retcode === 100) {
       console.log(`Go-cqhttp 发送通知消息异常 ${data.errmsg}\n`);
      } else {
       console.log(`Go-cqhttp 发送通知消息异常 ${JSON.stringify(data)}`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function serverNotify(text, desp) {
 return new Promise((resolve) => {
  const { PUSH_KEY } = push_config;
  if (PUSH_KEY) {
   // 微信server酱推送通知一个\n不会换行，需要两个\n才能换行，故做此替换
   desp = desp.replace(/[\n\r]/g, '\n\n');

   const matchResult = PUSH_KEY.match(/^sctp(\d+)t/i);
   const options = {
    url: matchResult && matchResult[1]
    ? `https://${matchResult[1]}.push.ft07.com/send/${PUSH_KEY}.send`
    : `https://sctapi.ftqq.com/${PUSH_KEY}.send`,
    body: `text=${encodeURIComponent(text)}&desp=${encodeURIComponent(desp)}`,
    headers: {
     'Content-Type': 'application/x-www-form-urlencoded',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('Server 酱发送通知调用API失败😞\n', err);
     } else {
      // server酱和Server酱·Turbo版的返回json格式不太一样
      if (data.errno === 0 || data.data.errno === 0) {
       console.log('Server 酱发送通知消息成功🎉\n');
      } else if (data.errno === 1024) {
       // 一分钟内发送相同的内容会触发
       console.log(`Server 酱发送通知消息异常 ${data.errmsg}\n`);
      } else {
       console.log(`Server 酱发送通知消息异常 ${JSON.stringify(data)}`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function pushDeerNotify(text, desp) {
 return new Promise((resolve) => {
  const { DEER_KEY, DEER_URL } = push_config;
  if (DEER_KEY) {
   // PushDeer 建议对消息内容进行 urlencode
   desp = encodeURI(desp);
   const options = {
    url: DEER_URL || `https://api2.pushdeer.com/message/push`,
    body: `pushkey=${DEER_KEY}&text=${text}&desp=${desp}&type=markdown`,
    headers: {
     'Content-Type': 'application/x-www-form-urlencoded',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('PushDeer 通知调用API失败😞\n', err);
     } else {
      // 通过返回的result的长度来判断是否成功
      if (
       data.content.result.length !== undefined &&
       data.content.result.length > 0
      ) {
       console.log('PushDeer 发送通知消息成功🎉\n');
      } else {
       console.log(
        `PushDeer 发送通知消息异常😞 ${JSON.stringify(data)}`,
       );
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function chatNotify(text, desp) {
 return new Promise((resolve) => {
  const { CHAT_URL, CHAT_TOKEN } = push_config;
  if (CHAT_URL && CHAT_TOKEN) {
   // 对消息内容进行 urlencode
   desp = encodeURI(desp);
   const options = {
    url: `${CHAT_URL}${CHAT_TOKEN}`,
    body: `payload={"text":"${text}\n${desp}"}`,
    headers: {
     'Content-Type': 'application/x-www-form-urlencoded',
    },
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('Chat 发送通知调用API失败😞\n', err);
     } else {
      if (data.success) {
       console.log('Chat 发送通知消息成功🎉\n');
      } else {
       console.log(`Chat 发送通知消息异常 ${JSON.stringify(data)}`);
      }
     }
    } catch (e) {
     $.logErr(e);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function barkNotify(text, desp, params = {}) {
 return new Promise((resolve) => {
  let {
   BARK_PUSH,
   BARK_ICON,
   BARK_SOUND,
   BARK_GROUP,
   BARK_LEVEL,
   BARK_ARCHIVE,
   BARK_URL,
  } = push_config;
  if (BARK_PUSH) {
   // 兼容BARK本地用户只填写设备码的情况
   if (!BARK_PUSH.startsWith('http')) {
    BARK_PUSH = `https://api.day.app/${BARK_PUSH}`;
   }
   const options = {
    url: `${BARK_PUSH}`,
    json: {
     title: text,
     body: desp,
     icon: BARK_ICON,
     sound: BARK_SOUND,
     group: BARK_GROUP,
     isArchive: BARK_ARCHIVE,
     level: BARK_LEVEL,
     url: BARK_URL,
     ...params,
    },
    headers: {
     'Content-Type': 'application/json',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('Bark APP 发送通知调用API失败😞\n', err);
     } else {
      if (data.code === 200) {
       console.log('Bark APP 发送通知消息成功🎉\n');
      } else {
       console.log(`Bark APP 发送通知消息异常 ${data.message}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve();
    }
   });
  } else {
   resolve();
  }
 });
}

function tgBotNotify(text, desp) {
 return new Promise((resolve) => {
  const {
   TG_BOT_TOKEN,
   TG_USER_ID,
   TG_PROXY_HOST,
   TG_PROXY_PORT,
   TG_API_HOST,
   TG_PROXY_AUTH,
  } = push_config;
  if (TG_BOT_TOKEN && TG_USER_ID) {
   const options = {
    url: `${TG_API_HOST}/bot${TG_BOT_TOKEN}/sendMessage`,
    json: {
     chat_id: `${TG_USER_ID}`,
     text: `${text}\n\n${desp}`,
     disable_web_page_preview: true,
    },
    headers: {
     'Content-Type': 'application/json',
    },
    timeout,
   };
   if (TG_PROXY_HOST && TG_PROXY_PORT) {
    const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
    const options = {
     keepAlive: true,
     keepAliveMsecs: 1000,
     maxSockets: 256,
     maxFreeSockets: 256,
     proxy: `http://${TG_PROXY_AUTH}${TG_PROXY_HOST}:${TG_PROXY_PORT}`,
    };
    const httpAgent = new HttpProxyAgent(options);
    const httpsAgent = new HttpsProxyAgent(options);
    const agent = {
     http: httpAgent,
     https: httpsAgent,
    };
    Object.assign(options, { agent });
   }
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('Telegram 发送通知消息失败😞\n', err);
     } else {
      if (data.ok) {
       console.log('Telegram 发送通知消息成功🎉。\n');
      } else if (data.error_code === 400) {
       console.log(
        '请主动给bot发送一条消息并检查接收用户ID是否正确。\n',
       );
      } else if (data.error_code === 401) {
       console.log('Telegram bot token 填写错误。\n');
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}
function ddBotNotify(text, desp) {
 return new Promise((resolve) => {
  const { DD_BOT_TOKEN, DD_BOT_SECRET } = push_config;
  const options = {
   url: `https://oapi.dingtalk.com/robot/send?access_token=${DD_BOT_TOKEN}`,
   json: {
    msgtype: 'text',
    text: {
     content: `${text}\n\n${desp}`,
    },
   },
   headers: {
    'Content-Type': 'application/json',
   },
   timeout,
  };
  if (DD_BOT_TOKEN && DD_BOT_SECRET) {
   const crypto = require('crypto');
   const dateNow = Date.now();
   const hmac = crypto.createHmac('sha256', DD_BOT_SECRET);
   hmac.update(`${dateNow}\n${DD_BOT_SECRET}`);
   const result = encodeURIComponent(hmac.digest('base64'));
   options.url = `${options.url}&timestamp=${dateNow}&sign=${result}`;
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('钉钉发送通知消息失败😞\n', err);
     } else {
      if (data.errcode === 0) {
       console.log('钉钉发送通知消息成功🎉\n');
      } else {
       console.log(`钉钉发送通知消息异常 ${data.errmsg}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else if (DD_BOT_TOKEN) {
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('钉钉发送通知消息失败😞\n', err);
     } else {
      if (data.errcode === 0) {
       console.log('钉钉发送通知消息成功🎉\n');
      } else {
       console.log(`钉钉发送通知消息异常 ${data.errmsg}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function qywxBotNotify(text, desp) {
 return new Promise((resolve) => {
  const { QYWX_ORIGIN, QYWX_KEY } = push_config;
  const options = {
   url: `${QYWX_ORIGIN}/cgi-bin/webhook/send?key=${QYWX_KEY}`,
   json: {
    msgtype: 'text',
    text: {
     content: `${text}\n\n${desp}`,
    },
   },
   headers: {
    'Content-Type': 'application/json',
   },
   timeout,
  };
  if (QYWX_KEY) {
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('企业微信发送通知消息失败😞\n', err);
     } else {
      if (data.errcode === 0) {
       console.log('企业微信发送通知消息成功🎉。\n');
      } else {
       console.log(`企业微信发送通知消息异常 ${data.errmsg}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function ChangeUserId(desp) {
 const { QYWX_AM } = push_config;
 const QYWX_AM_AY = QYWX_AM.split(',');
 if (QYWX_AM_AY[2]) {
  const userIdTmp = QYWX_AM_AY[2].split('|');
  let userId = '';
  for (let i = 0; i < userIdTmp.length; i++) {
   const count = '账号' + (i + 1);
   const count2 = '签到号 ' + (i + 1);
   if (desp.match(count2)) {
    userId = userIdTmp[i];
   }
  }
  if (!userId) userId = QYWX_AM_AY[2];
  return userId;
 } else {
  return '@all';
 }
}

async function qywxamNotify(text, desp) {
 const MAX_LENGTH = 900;
 if (desp.length > MAX_LENGTH) {
  let d = desp.substr(0, MAX_LENGTH) + '\n==More==';
  await do_qywxamNotify(text, d);
  await qywxamNotify(text, desp.substr(MAX_LENGTH));
 } else {
  return await do_qywxamNotify(text, desp);
 }
}

function do_qywxamNotify(text, desp) {
 return new Promise((resolve) => {
  const { QYWX_AM, QYWX_ORIGIN } = push_config;
  if (QYWX_AM) {
   const QYWX_AM_AY = QYWX_AM.split(',');
   const options_accesstoken = {
    url: `${QYWX_ORIGIN}/cgi-bin/gettoken`,
    json: {
     corpid: `${QYWX_AM_AY[0]}`,
     corpsecret: `${QYWX_AM_AY[1]}`,
    },
    headers: {
     'Content-Type': 'application/json',
    },
    timeout,
   };
   $.post(options_accesstoken, (err, resp, json) => {
    let html = desp.replace(/\n/g, '<br/>');
    let accesstoken = json.access_token;
    let options;

    switch (QYWX_AM_AY[4]) {
     case '0':
      options = {
       msgtype: 'textcard',
       textcard: {
        title: `${text}`,
        description: `${desp}`,
        url: 'https://github.com/whyour/qinglong',
        btntxt: '更多',
       },
      };
      break;

     case '1':
      options = {
       msgtype: 'text',
       text: {
        content: `${text}\n\n${desp}`,
       },
      };
      break;

     default:
      options = {
       msgtype: 'mpnews',
       mpnews: {
        articles: [
         {
          title: `${text}`,
          thumb_media_id: `${QYWX_AM_AY[4]}`,
          author: `智能助手`,
          content_source_url: ``,
          content: `${html}`,
          digest: `${desp}`,
         },
        ],
       },
      };
    }
    if (!QYWX_AM_AY[4]) {
     // 如不提供第四个参数,则默认进行文本消息类型推送
     options = {
      msgtype: 'text',
      text: {
       content: `${text}\n\n${desp}`,
      },
     };
    }
    options = {
     url: `${QYWX_ORIGIN}/cgi-bin/message/send?access_token=${accesstoken}`,
     json: {
      touser: `${ChangeUserId(desp)}`,
      agentid: `${QYWX_AM_AY[3]}`,
      safe: '0',
      ...options,
     },
     headers: {
      'Content-Type': 'application/json',
     },
    };

    $.post(options, (err, resp, data) => {
     try {
      if (err) {
       console.log(
        '成员ID:' +
         ChangeUserId(desp) +
         '企业微信应用消息发送通知消息失败😞\n',
        err,
       );
      } else {
       if (data.errcode === 0) {
        console.log(
         '成员ID:' +
          ChangeUserId(desp) +
          '企业微信应用消息发送通知消息成功🎉。\n',
        );
       } else {
        console.log(
         `企业微信应用消息发送通知消息异常 ${data.errmsg}\n`,
        );
       }
      }
     } catch (e) {
      $.logErr(e, resp);
     } finally {
      resolve(data);
     }
    });
   });
  } else {
   resolve();
  }
 });
}

function iGotNotify(text, desp, params = {}) {
 return new Promise((resolve) => {
  const { IGOT_PUSH_KEY } = push_config;
  if (IGOT_PUSH_KEY) {
   // 校验传入的IGOT_PUSH_KEY是否有效
   const IGOT_PUSH_KEY_REGX = new RegExp('^[a-zA-Z0-9]{24}$');
   if (!IGOT_PUSH_KEY_REGX.test(IGOT_PUSH_KEY)) {
    console.log('您所提供的 IGOT_PUSH_KEY 无效\n');
    resolve();
    return;
   }
   const options = {
    url: `https://push.hellyw.com/${IGOT_PUSH_KEY.toLowerCase()}`,
    body: `title=${text}&content=${desp}&${querystring.stringify(params)}`,
    headers: {
     'Content-Type': 'application/x-www-form-urlencoded',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('IGot 发送通知调用API失败😞\n', err);
     } else {
      if (data.ret === 0) {
       console.log('IGot 发送通知消息成功🎉\n');
      } else {
       console.log(`IGot 发送通知消息异常 ${data.errMsg}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function pushPlusNotify(text, desp) {
 return new Promise((resolve) => {
  const { PUSH_PLUS_TOKEN, PUSH_PLUS_USER, PUSH_PLUS_TEMPLATE, PUSH_PLUS_CHANNEL, PUSH_PLUS_WEBHOOK, PUSH_PLUS_CALLBACKURL, PUSH_PLUS_TO } = push_config;
  if (PUSH_PLUS_TOKEN) {
   desp = desp.replace(/[\n\r]/g, '<br>'); // 默认为html, 不支持plaintext
   const body = {
    token: `${PUSH_PLUS_TOKEN}`,
    title: `${text}`,
    content: `${desp}`,
    topic: `${PUSH_PLUS_USER}`,
    template: `${PUSH_PLUS_TEMPLATE}`,
    channel: `${PUSH_PLUS_CHANNEL}`,
    webhook: `${PUSH_PLUS_WEBHOOK}`,
    callbackUrl: `${PUSH_PLUS_CALLBACKURL}`,
    to: `${PUSH_PLUS_TO}`
   };
   const options = {
    url: `https://www.pushplus.plus/send`,
    body: JSON.stringify(body),
    headers: {
     'Content-Type': ' application/json',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log(
       `pushplus 发送${
        PUSH_PLUS_USER ? '一对多' : '一对一'
       }通知消息失败😞\n`,
       err,
      );
     } else {
      if (data.code === 200) {
       console.log(
        `pushplus 发送${
         PUSH_PLUS_USER ? '一对多' : '一对一'
        }通知请求成功🎉，可根据流水号查询推送结果：${data.data}\n注意：请求成功并不代表推送成功，如未收到消息，请到pushplus官网使用流水号查询推送最终结果`,
       );
      } else {
       console.log(
        `pushplus 发送${
         PUSH_PLUS_USER ? '一对多' : '一对一'
        }通知消息异常 ${data.msg}\n`,
       );
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function wePlusBotNotify(text, desp) {
 return new Promise((resolve) => {
  const { WE_PLUS_BOT_TOKEN, WE_PLUS_BOT_RECEIVER, WE_PLUS_BOT_VERSION } =
   push_config;
  if (WE_PLUS_BOT_TOKEN) {
   let template = 'txt';
   if (desp.length > 800) {
    desp = desp.replace(/[\n\r]/g, '<br>');
    template = 'html';
   }
   const body = {
    token: `${WE_PLUS_BOT_TOKEN}`,
    title: `${text}`,
    content: `${desp}`,
    template: `${template}`,
    receiver: `${WE_PLUS_BOT_RECEIVER}`,
    version: `${WE_PLUS_BOT_VERSION}`,
   };
   const options = {
    url: `https://www.weplusbot.com/send`,
    body: JSON.stringify(body),
    headers: {
     'Content-Type': ' application/json',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log(`微加机器人发送通知消息失败😞\n`, err);
     } else {
      if (data.code === 200) {
       console.log(`微加机器人发送通知消息完成🎉\n`);
      } else {
       console.log(`微加机器人发送通知消息异常 ${data.msg}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function aibotkNotify(text, desp) {
 return new Promise((resolve) => {
  const { AIBOTK_KEY, AIBOTK_TYPE, AIBOTK_NAME } = push_config;
  if (AIBOTK_KEY && AIBOTK_TYPE && AIBOTK_NAME) {
   let json = {};
   let url = '';
   switch (AIBOTK_TYPE) {
    case 'room':
     url = 'https://api-bot.aibotk.com/openapi/v1/chat/room';
     json = {
      apiKey: `${AIBOTK_KEY}`,
      roomName: `${AIBOTK_NAME}`,
      message: {
       type: 1,
       content: `【青龙快讯】\n\n${text}\n${desp}`,
      },
     };
     break;
    case 'contact':
     url = 'https://api-bot.aibotk.com/openapi/v1/chat/contact';
     json = {
      apiKey: `${AIBOTK_KEY}`,
      name: `${AIBOTK_NAME}`,
      message: {
       type: 1,
       content: `【青龙快讯】\n\n${text}\n${desp}`,
      },
     };
     break;
   }
   const options = {
    url: url,
    json,
    headers: {
     'Content-Type': 'application/json',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('智能微秘书发送通知消息失败😞\n', err);
     } else {
      if (data.code === 0) {
       console.log('智能微秘书发送通知消息成功🎉。\n');
      } else {
       console.log(`智能微秘书发送通知消息异常 ${data.error}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function fsBotNotify(text, desp) {
 return new Promise((resolve) => {
  const { FSKEY } = push_config;
  if (FSKEY) {
   const options = {
    url: `https://open.feishu.cn/open-apis/bot/v2/hook/${FSKEY}`,
    json: { msg_type: 'text', content: { text: `${text}\n\n${desp}` } },
    headers: {
     'Content-Type': 'application/json',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('飞书发送通知调用API失败😞\n', err);
     } else {
      if (data.StatusCode === 0 || data.code === 0) {
       console.log('飞书发送通知消息成功🎉\n');
      } else {
       console.log(`飞书发送通知消息异常 ${data.msg}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

async function smtpNotify(text, desp) {
 const { SMTP_EMAIL, SMTP_PASSWORD, SMTP_SERVICE, SMTP_NAME } = push_config;
 if (![SMTP_EMAIL, SMTP_PASSWORD].every(Boolean) || !SMTP_SERVICE) {
  return;
 }

 try {
  const nodemailer = require('nodemailer');
  const transporter = nodemailer.createTransport({
   service: SMTP_SERVICE,
   auth: {
    user: SMTP_EMAIL,
    pass: SMTP_PASSWORD,
   },
  });

  const addr = SMTP_NAME ? `"${SMTP_NAME}" <${SMTP_EMAIL}>` : SMTP_EMAIL;
  const info = await transporter.sendMail({
   from: addr,
   to: addr,
   subject: text,
   html: `${desp.replace(/\n/g, '<br/>')}`,
  });

  transporter.close();

  if (info.messageId) {
   console.log('SMTP 发送通知消息成功🎉\n');
   return true;
  }
  console.log('SMTP 发送通知消息失败😞\n');
 } catch (e) {
  console.log('SMTP 发送通知消息出现异常😞\n', e);
 }
}

function pushMeNotify(text, desp, params = {}) {
 return new Promise((resolve) => {
  const { PUSHME_KEY, PUSHME_URL } = push_config;
  if (PUSHME_KEY) {
   const options = {
    url: PUSHME_URL || 'https://push.i-i.me',
    json: { push_key: PUSHME_KEY, title: text, content: desp, ...params },
    headers: {
     'Content-Type': 'application/json',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('PushMe 发送通知调用API失败😞\n', err);
     } else {
      if (data === 'success') {
       console.log('PushMe 发送通知消息成功🎉\n');
      } else {
       console.log(`PushMe 发送通知消息异常 ${data}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function chronocatNotify(title, desp) {
 return new Promise((resolve) => {
  const { CHRONOCAT_TOKEN, CHRONOCAT_QQ, CHRONOCAT_URL } = push_config;
  if (!CHRONOCAT_TOKEN || !CHRONOCAT_QQ || !CHRONOCAT_URL) {
   resolve();
   return;
  }

  const user_ids = CHRONOCAT_QQ.match(/user_id=(\d+)/g)?.map(
   (match) => match.split('=')[1],
  );
  const group_ids = CHRONOCAT_QQ.match(/group_id=(\d+)/g)?.map(
   (match) => match.split('=')[1],
  );

  const url = `${CHRONOCAT_URL}/api/message/send`;
  const headers = {
   'Content-Type': 'application/json',
   Authorization: `Bearer ${CHRONOCAT_TOKEN}`,
  };

  for (const [chat_type, ids] of [
   [1, user_ids],
   [2, group_ids],
  ]) {
   if (!ids) {
    continue;
   }
   for (const chat_id of ids) {
    const data = {
     peer: {
      chatType: chat_type,
      peerUin: chat_id,
     },
     elements: [
      {
       elementType: 1,
       textElement: {
        content: `${title}\n\n${desp}`,
       },
      },
     ],
    };
    const options = {
     url: url,
     json: data,
     headers,
     timeout,
    };
    $.post(options, (err, resp, data) => {
     try {
      if (err) {
       console.log('Chronocat 发送QQ通知消息失败😞\n', err);
      } else {
       if (chat_type === 1) {
        console.log(`Chronocat 个人消息 ${ids}推送成功🎉`);
       } else {
        console.log(`Chronocat 群消息 ${ids}推送成功🎉`);
       }
      }
     } catch (e) {
      $.logErr(e, resp);
     } finally {
      resolve(data);
     }
    });
   }
  }
 });
}

function qmsgNotify(text, desp) {
 return new Promise((resolve) => {
  const { QMSG_KEY, QMSG_TYPE } = push_config;
  if (QMSG_KEY && QMSG_TYPE) {
   const options = {
    url: `https://qmsg.zendee.cn/${QMSG_TYPE}/${QMSG_KEY}`,
    body: `msg=${text}\n\n${desp.replace('----', '-')}`,
    headers: {
     'Content-Type': 'application/x-www-form-urlencoded',
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('Qmsg 发送通知调用API失败😞\n', err);
     } else {
      if (data.code === 0) {
       console.log('Qmsg 发送通知消息成功🎉\n');
      } else {
       console.log(`Qmsg 发送通知消息异常 ${data}\n`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function webhookNotify(text, desp) {
 return new Promise((resolve) => {
  const {
   WEBHOOK_URL,
   WEBHOOK_BODY,
   WEBHOOK_HEADERS,
   WEBHOOK_CONTENT_TYPE,
   WEBHOOK_METHOD,
  } = push_config;
  if (
   !WEBHOOK_METHOD ||
   !WEBHOOK_URL ||
   (!WEBHOOK_URL.includes('$title') && !WEBHOOK_BODY.includes('$title'))
  ) {
   resolve();
   return;
  }

  const headers = parseHeaders(WEBHOOK_HEADERS);
  const body = parseBody(WEBHOOK_BODY, WEBHOOK_CONTENT_TYPE, (v) =>
   v
    ?.replaceAll('$title', text?.replaceAll('\n', '\\n'))
    ?.replaceAll('$content', desp?.replaceAll('\n', '\\n')),
  );
  const bodyParam = formatBodyFun(WEBHOOK_CONTENT_TYPE, body);
  const options = {
   method: WEBHOOK_METHOD,
   headers,
   allowGetBody: true,
   ...bodyParam,
   timeout,
   retry: 1,
  };

  const formatUrl = WEBHOOK_URL.replaceAll(
   '$title',
   encodeURIComponent(text),
  ).replaceAll('$content', encodeURIComponent(desp));
  got(formatUrl, options).then((resp) => {
   try {
    if (resp.statusCode !== 200) {
     console.log(`自定义发送通知消息失败😞 ${resp.body}\n`);
    } else {
     console.log(`自定义发送通知消息成功🎉 ${resp.body}\n`);
    }
   } catch (e) {
    $.logErr(e, resp);
   } finally {
    resolve(resp.body);
   }
  });
 });
}

function ntfyNotify(text, desp) {
 function encodeRFC2047(text) {
  const encodedBase64 = Buffer.from(text).toString('base64');
  return `=?utf-8?B?${encodedBase64}?=`;
 }

 return new Promise((resolve) => {
  const { NTFY_URL, NTFY_TOPIC, NTFY_PRIORITY } = push_config;
  if (NTFY_TOPIC) {
   const options = {
    url: `${NTFY_URL || 'https://ntfy.sh'}/${NTFY_TOPIC}`,
    body: `${desp}`, 
    headers: {
     'Title': `${encodeRFC2047(text)}`,
     'Priority': NTFY_PRIORITY || '3'
    },
    timeout,
   };
   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('Ntfy 通知调用API失败😞\n', err);
     } else {
      if (data.id) {
       console.log('Ntfy 发送通知消息成功🎉\n');
      } else {
       console.log(`Ntfy 发送通知消息异常 ${JSON.stringify(data)}`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   resolve();
  }
 });
}

function wxPusherNotify(text, desp) {
 return new Promise((resolve) => {
  const { WXPUSHER_APP_TOKEN, WXPUSHER_TOPIC_IDS, WXPUSHER_UIDS } = push_config;
  if (WXPUSHER_APP_TOKEN) {
   // 处理topic_ids，将分号分隔的字符串转为数组
   const topicIds = WXPUSHER_TOPIC_IDS ? WXPUSHER_TOPIC_IDS.split(';')
    .map(id => id.trim())
    .filter(id => id)
    .map(id => parseInt(id)) : [];

   // 处理uids，将分号分隔的字符串转为数组
   const uids = WXPUSHER_UIDS ? WXPUSHER_UIDS.split(';')
    .map(uid => uid.trim())
    .filter(uid => uid) : [];

   // topic_ids uids 至少有一个
   if (!topicIds.length && !uids.length) {
    console.log("wxpusher 服务的 WXPUSHER_TOPIC_IDS 和 WXPUSHER_UIDS 至少设置一个!!\n取消推送");
    return resolve();
   }

   const body = {
    appToken: WXPUSHER_APP_TOKEN,
    content: `<h1>${text}</h1><br/><div style='white-space: pre-wrap;'>${desp}</div>`,
    summary: text,
    contentType: 2,
    topicIds: topicIds,
    uids: uids,
    verifyPayType: 0
   };

   const options = {
    url: 'https://wxpusher.zjiecode.com/api/send/message',
    body: JSON.stringify(body),
    headers: {
     'Content-Type': 'application/json'
    },
    timeout
   };

   $.post(options, (err, resp, data) => {
    try {
     if (err) {
      console.log('wxpusher发送通知消息失败！\n', err);
     } else {
      if (data.code === 1000) {
       console.log('wxpusher发送通知消息完成！');
      } else {
       console.log(`wxpusher发送通知消息异常：${data.msg}`);
      }
     }
    } catch (e) {
     $.logErr(e, resp);
    } finally {
     resolve(data);
    }
   });
  } else {
   console.log('wxpusher 服务的 WXPUSHER_APP_TOKEN 未设置!!\n取消推送');
   resolve();
  }
 });
}


function parseString(input, valueFormatFn) {
 const regex = /(\w+):\s*((?:(?!\n\w+:).)*)/g;
 const matches = {};

 let match;
 while ((match = regex.exec(input)) !== null) {
  const [, key, value] = match;
  const _key = key.trim();
  if (!_key || matches[_key]) {
   continue;
  }

  let _value = value.trim();

  try {
   _value = valueFormatFn ? valueFormatFn(_value) : _value;
   const jsonValue = JSON.parse(_value);
   matches[_key] = jsonValue;
  } catch (error) {
   matches[_key] = _value;
  }
 }

 return matches;
}

function parseHeaders(headers) {
 if (!headers) return {};

 const parsed = {};
 let key;
 let val;
 let i;

 headers &&
  headers.split('\n').forEach(function parser(line) {
   i = line.indexOf(':');
   key = line.substring(0, i).trim().toLowerCase();
   val = line.substring(i + 1).trim();

   if (!key) {
    return;
   }

   parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
  });

 return parsed;
}

function parseBody(body, contentType, valueFormatFn) {
 if (contentType === 'text/plain' || !body) {
  return valueFormatFn && body ? valueFormatFn(body) : body;
 }

 const parsed = parseString(body, valueFormatFn);

 switch (contentType) {
  case 'multipart/form-data':
   return Object.keys(parsed).reduce((p, c) => {
    p.append(c, parsed[c]);
    return p;
   }, new FormData());
  case 'application/x-www-form-urlencoded':
   return Object.keys(parsed).reduce((p, c) => {
    return p ? `${p}&${c}=${parsed[c]}` : `${c}=${parsed[c]}`;
   });
 }

 return parsed;
}

function formatBodyFun(contentType, body) {
 if (!body) return {};
 switch (contentType) {
  case 'application/json':
   return { json: body };
  case 'multipart/form-data':
   return { form: body };
  case 'application/x-www-form-urlencoded':
  case 'text/plain':
   return { body };
 }
 return {};
}

/**
* sendNotify 推送通知功能
* @param text 通知头
* @param desp 通知体
* @param params 某些推送通知方式点击弹窗可跳转, 例：{ url: 'https://abc.com' }
* @returns {Promise<unknown>}
*/
async function sendNotify(text, desp, params = {}) {
 // 根据标题跳过一些消息推送，环境变量：SKIP_PUSH_TITLE 用回车分隔
 let skipTitle = process.env.SKIP_PUSH_TITLE;
 if (skipTitle) {
  if (skipTitle.split('\n').includes(text)) {
   console.info(text + '在 SKIP_PUSH_TITLE 环境变量内，跳过推送');
   return;
  }
 }

 if (push_config.HITOKOTO !== 'false') {
  desp += '\n\n' + (await one());
 }

 await Promise.all([
  serverNotify(text, desp), // 微信server酱
  pushPlusNotify(text, desp), // pushplus
  wePlusBotNotify(text, desp), // 微加机器人
  barkNotify(text, desp, params), // iOS Bark APP
  tgBotNotify(text, desp), // telegram 机器人
  ddBotNotify(text, desp), // 钉钉机器人
  qywxBotNotify(text, desp), // 企业微信机器人
  qywxamNotify(text, desp), // 企业微信应用消息推送
  iGotNotify(text, desp, params), // iGot
  gobotNotify(text, desp), // go-cqhttp
  gotifyNotify(text, desp), // gotify
  chatNotify(text, desp), // synolog chat
  pushDeerNotify(text, desp), // PushDeer
  aibotkNotify(text, desp), // 智能微秘书
  fsBotNotify(text, desp), // 飞书机器人
  smtpNotify(text, desp), // SMTP 邮件
  pushMeNotify(text, desp, params), // PushMe
  chronocatNotify(text, desp), // Chronocat
  webhookNotify(text, desp), // 自定义通知
  qmsgNotify(text, desp), // 自定义通知
  ntfyNotify(text, desp), // Ntfy
  wxPusherNotify(text, desp), // wxpusher
 ]);
}

module.exports = {
 sendNotify,
};

